package com.lap.web.starter.api.builder;

import com.lap.framework.common.annotation.AuthSkip;
import com.lap.web.starter.api.ApiResource;
import com.lap.web.starter.api.MappingRegistry;
import com.lap.web.starter.api.valueobject.AccessLevel;
import com.lap.web.starter.api.valueobject.ApiType;
import com.lap.web.starter.shared.Constant;
import com.lap.web.starter.shared.UriUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 默认资源构造
 *
 * @author Shuisheng Lao(劳水生)
 * @version 0.0.1
 */
class DefaultBuilder extends ApiBuilder {
  private final List<ApiResource> resourceList = new ArrayList<>();

  @Override
  public void builderModule(String module, String name) {
    ApiResource apiResource =
        buildApi(module, name, module, ApiType.MODULE, null, null, AccessLevel.LIMIT, module);
    resourceList.add(apiResource);
  }

  @Override
  public void builderClass(String module, String name, List<Object> dataList) {
    if (CollectionUtils.isEmpty(dataList)) return;

    for (Object object : dataList) {
      if (object == null) continue;

      ApiResource apiResource = buildClass(module, object.getClass());
      List<ApiResource> methodList =
          Stream.of(object.getClass().getMethods())
              .map(method -> buildMethod(apiResource, method))
              .filter(Objects::nonNull)
              .toList();

      if (!methodList.isEmpty()) {
        resourceList.add(apiResource);
        resourceList.addAll(methodList);
      }
    }
  }

  private ApiResource buildClass(String module, Class<?> clazz) {
    String basePath = resolveMapping(clazz);
    String name = resolveTagName(clazz);
    String code = UriUtil.getClassName(clazz);

    return buildApi(
        module, name, basePath, ApiType.CLAZZ, module, basePath, AccessLevel.LIMIT, code);
  }

  private ApiResource buildMethod(ApiResource parent, Method method) {
    String name = resolveMethodName(method);
    AccessLevel accessLevel = resolveAccessLevel(method);

    ApiResource apiResource =
        buildApi(
            parent.getModule(),
            name,
            parent.getUri(),
            ApiType.METHOD,
            parent.getCode(),
            parent.getBasePath(),
            accessLevel,
            null);

    return MappingRegistry.process(method, parent.getUri(), apiResource) ? apiResource : null;
  }

  private ApiResource buildApi(
      String module,
      String name,
      String uri,
      ApiType type,
      String parentCode,
      String basePath,
      AccessLevel accessLevel,
      String code) {
    return ApiResource.builder()
        .module(module)
        .name(name)
        .uri(uri)
        .type(type)
        .parentCode(parentCode)
        .basePath(basePath)
        .accessLevel(accessLevel)
        .code(code)
        .build();
  }

  private String resolveMapping(Class<?> clazz) {
    RequestMapping mapping = AnnotationUtils.findAnnotation(clazz, RequestMapping.class);
    return (mapping != null && mapping.value().length > 0) ? mapping.value()[0] : Constant.BLANK;
  }

  private String resolveTagName(Class<?> clazz) {
    Tag tag = clazz.getAnnotation(Tag.class);
    return (tag != null)
        ? StringUtils.defaultIfBlank(tag.name(), tag.description())
        : clazz.getSimpleName();
  }

  private String resolveMethodName(Method method) {
    Operation operation = method.getAnnotation(Operation.class);
    return (operation != null)
        ? StringUtils.defaultIfBlank(operation.summary(), operation.description())
        : method.getName();
  }

  private AccessLevel resolveAccessLevel(Method method) {
    return method.isAnnotationPresent(AuthSkip.class) ? AccessLevel.ALL : AccessLevel.LIMIT;
  }

  @Override
  public List<ApiResource> getApis() {
    return resourceList;
  }
}
